package pl.edu.agh.neuraleconomy.core.decision;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import lombok.NoArgsConstructor;
import lombok.Setter;

import org.apache.log4j.Logger;

import pl.edu.agh.neuraleconomy.core.exception.CoreException;
import pl.edu.agh.neuraleconomy.core.simulation.SimulationEngine;
import pl.edu.agh.neuraleconomy.core.simulation.SimulationService;
import pl.edu.agh.neuraleconomy.core.simulation.Transaction;
import pl.edu.agh.neuraleconomy.core.ta.advice.Advice;
import pl.edu.agh.neuraleconomy.core.ta.advice.AdviceType;
import pl.edu.agh.neuraleconomy.model.simulation.TransactionType;

@NoArgsConstructor
public class SimpleDecisionMaker implements IDecisionMaker {
	private Logger logger = Logger.getLogger(getClass());
	private SimulationService service = new SimulationService();

	@Setter
	private double moneyPart = 0.4;
	@Setter
	private double shortSellMoneyPart = 0.2;
	@Setter
	private int buyCertaintyThreshold = 70;
	@Setter
	private int sellCertaintyThreshold = 40;
	@Setter
	private int maxBuyTransactions = 4;
	@Setter
	private int maxShortSellTransactions = 2;
	
	public SimpleDecisionMaker(double moneyPart, double shortSellMoneyPart){
		this.moneyPart = moneyPart;
		this.shortSellMoneyPart = shortSellMoneyPart;
	}

	public List<Transaction> makeDecision(SimulationEngine simulation, List<Advice> advices) {
		List<Advice> buyAdvices = getAdvices(advices, AdviceType.BUY, buyCertaintyThreshold);
		List<Advice> sellAdvices = getAdvices(advices, AdviceType.SELL, sellCertaintyThreshold);

		List<Transaction> transactionsMade = new LinkedList<Transaction>();

		transactionsMade.addAll(processSellAdvices(simulation, sellAdvices));
		transactionsMade.addAll(processBuyAdvices(simulation, buyAdvices));

		return transactionsMade;
	}

	private List<Transaction> processSellAdvices(SimulationEngine simulation, List<Advice> sellAdvices) {
		List<Transaction> transactionsMade = new LinkedList<Transaction>();
		
		int shortSelledTransactions = 0;
		
		Collections.shuffle(sellAdvices);

		for (Advice advice : sellAdvices) {
			int sharesOwned = simulation.getSharesAmmount(advice.getCompany());

			if (sharesOwned > 0) {
				Transaction transaction = Transaction.getSellTransaction(advice.getCompany(), sharesOwned, simulation.getCurrentDate(),
						simulation, null);

				makeTransaction(transactionsMade, transaction, simulation);
			} else if(shortSelledTransactions < maxShortSellTransactions){
				
				int shortSellAmount = (int) (shortSellMoneyPart * simulation.getCurrentPureMoney().doubleValue() / service.getSharePrice(advice.getCompany(),
						simulation.getCurrentDate()).doubleValue());
				
				Transaction transaction = new Transaction(TransactionType.SELL_SHORT, advice.getCompany(), shortSellAmount, simulation.getCurrentDate(), simulation, null);
				makeTransaction(transactionsMade, transaction, simulation);
				
				shortSelledTransactions++;
			}
		}

		return transactionsMade;
	}

	private List<Transaction> processBuyAdvices(SimulationEngine simulation, List<Advice> buyAdvices) {
		List<Transaction> transactionsMade = new LinkedList<Transaction>();
		
		Collections.shuffle(buyAdvices);

		double toSpend4all = moneyToSpend(simulation);
		double certaintySum = (double) (maxBuyTransactions) *  90.0;

		//buyAdvices = buyAdvices.subList(0, Math.min(4, buyAdvices.size()));
		
		int buyTransactions = 0;

//		for (Advice advice : buyAdvices) {
//			certaintySum += advice.getCertainty();
//		}

		for (Advice advice : buyAdvices) {
			int shortSelled = simulation.getShortSelledSharesAmmount(advice.getCompany());

			if (shortSelled > 0) { // rebuy short selled shares

				Transaction transaction = Transaction.getBuyTransaction(advice.getCompany(), shortSelled, simulation.getCurrentDate(),
						simulation, null);
				makeTransaction(transactionsMade, transaction, simulation);

			} else if (buyTransactions < maxBuyTransactions){

				double toSpend = ((double) advice.getCertainty() / certaintySum) * toSpend4all;
				double price = service.getSharePrice(advice.getCompany(), simulation.getCurrentDate()).doubleValue();

				if (price == 0) {
					continue;
				}

				int sharesToBuy = (int) (toSpend / price);

				if (sharesToBuy > 100000) {
					int a = 3;

				}

				Transaction transaction = Transaction.getBuyTransaction(advice.getCompany(), sharesToBuy, simulation.getCurrentDate(),
						simulation, null);
				makeTransaction(transactionsMade, transaction, simulation);
				
				buyTransactions++;
			}
		}

		return transactionsMade;
	}

	private void makeTransaction(List<Transaction> transactionsMade, Transaction transaction, SimulationEngine simulation) {
		try {
			simulation.makeTransaction(transaction);
			transactionsMade.add(transaction);
			logger.debug(String.format("Making transaction: %s", transaction.toString()));
		} catch (CoreException e) {
			logger.warn(String.format("Could not make transaction: %s", transaction), e);
		}
	}

	private double moneyToSpend(SimulationEngine simulation) {
		return moneyPart * simulation.getCurrentPureMoney().doubleValue();
	}

	private List<Advice> getAdvices(List<Advice> advices, AdviceType type, int certaintyThreshold) {
		List<Advice> result = new LinkedList<Advice>();

		for (Advice advice : advices) {
			if (advice != null && type.equals(advice.getType()) && advice.getCertainty() > certaintyThreshold) {
				result.add(advice);
			}
		}

		return result;
	}

}
